/*
 * Copyright 2015 Zhongan.com All right reserved. This software is the
 * confidential and proprietary information of Zhongan.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Zhongan.com.
 */
package net.wicp.tams.common.apiext;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;

import lombok.extern.slf4j.Slf4j;

/****
 * 
 * @author zhoujunhui
 *
 */
@Slf4j
public class TarUtil {

	/***
	 * 解压tar文件
	 * 
	 * @param tarFilePath
	 *            tar的路径
	 * @param directory
	 *            解压后的存放路径
	 */
	public static void decompress(String tarFilePath, String directory) {

		File tarFile = new File(tarFilePath);
		if (!tarFile.exists()) {
			throw new RuntimeException("tar file is not exists.");
		}

		if (!tarFile.isFile()) {
			throw new RuntimeException("compress file is not a file.");
		}
		try {
			InputStream ins = new FileInputStream(tarFile);
			decompress(ins, directory);
		} catch (FileNotFoundException e) {
			log.error("没有这个文件:{}", tarFilePath);
		}
	}

	/***
	 * 解压tar到当前目录下
	 * 
	 * @param tarFilePath
	 *            tar文件的路径
	 */
	public static void decompress(String tarFilePath) {
		tarFilePath = tarFilePath.replace("\\", "/");
		int index = tarFilePath.lastIndexOf("/");
		decompress(tarFilePath, tarFilePath.substring(0, index));
	}

	public static void decompress(InputStream is, String directory) {
		TarArchiveInputStream in = null;
		try {
			in = new TarArchiveInputStream(is, "utf-8");
			TarArchiveEntry entry = in.getNextTarEntry();
			while (entry != null) {
				if (entry.isDirectory()) {
					entry = in.getNextTarEntry();
					continue;
				}
				File curfile = new File(directory, entry.getName());
				File parent = curfile.getParentFile();
				if (!parent.exists()) {
					parent.mkdirs();
				}
				OutputStream out = null;
				try {
					out = new FileOutputStream(curfile);
					IOUtils.copy(in, out);
					entry = in.getNextTarEntry();
				} finally {
					out.close();
				}
			}
			in.close();

		} catch (Exception e) {
			log.error("compress tar error.", e);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					log.error("compress tar error.", e);
				}
			}
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					log.error("compress tar error.", e);
				}
			}
		}
	}

	public static void decompress(byte[] bytes, String directory) {
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
		decompress(bais, directory);
	}

	/////////////////////////////////////////////////////////////////////////////////// 来自网络//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	private static final String TAG = TarUtil.class.getName();
	private static final int BUFFER = 1024;
	private static String mCompressSrcParentPath = "";

	/**
	 * usage: compress("/mnt/sdcard/Download", "/mnt/sdcard/Download.tar");
	 * 
	 * @param srcAbsolutePath
	 *            can be path of file or directory
	 * @param destAbsolutePath
	 *            suggest ending with a ".tar" suffix
	 */
	public static void compress(String srcAbsolutePath, String destAbsolutePath) {
		compress(new File(srcAbsolutePath), destAbsolutePath);
	}

	/**
	 * @param srcFile
	 *            can be file or directory
	 * @param destAbsolutePath
	 *            suggest ending with a ".tar" suffix
	 */
	public static void compress(File srcFile, String destAbsolutePath) {
		File destFile = new File(destAbsolutePath);
		try {
			if (!destFile.exists()) {
				destFile.createNewFile();
			}
			TarArchiveOutputStream taos = new TarArchiveOutputStream(new FileOutputStream(destFile));
			mCompressSrcParentPath = srcFile.getParent() + File.separator;
			compress(srcFile, taos);
			taos.flush();
			taos.close();
		} catch (IOException e) {
			log.info(TAG, e.toString());
		}
	}

	private static void compress(File file, TarArchiveOutputStream taos) {
		if (file.isDirectory()) {
			compressDir(file, taos);
		} else {
			compressFile(file, taos);
		}
	}

	private static void compressDir(File dir, TarArchiveOutputStream taos) {
		File[] files = dir.listFiles();
		if (files != null) {
			if (files.length == 0) {
				try {
					taos.putArchiveEntry(createTarArchiveEntry(dir));
					taos.closeArchiveEntry();
				} catch (IOException e) {
					log.info(TAG, e.toString());
				}
			} else {
				for (File file : files) {
					compress(file, taos);
				}
			}
		}
	}

	private static TarArchiveEntry createTarArchiveEntry(File file) {
		/** 不能用绝对路径，举例对于压缩/mnt/sdcard下的abc文件，如果不做截短，压缩文件里面会在abc外面套mnt和sdcard两层空壳，所以要把abc所属路径的/mnt/sdcard去掉 **/
		return new TarArchiveEntry(file, file.getPath().replace(mCompressSrcParentPath, ""));
	}

	private static void compressFile(File file, TarArchiveOutputStream taos) {
		try {
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			taos.putArchiveEntry(createTarArchiveEntry(file));
			int count;
			byte data[] = new byte[BUFFER];
			while ((count = bis.read(data, 0, BUFFER)) != -1) {
				taos.write(data, 0, count);
			}
			bis.close();
			taos.closeArchiveEntry();
		} catch (IOException e) {
			log.info(TAG, e.toString());
		}
	}

	// usage: extract("/mnt/sdcard/Download.tar", "/mnt/sdcard") 
	public static void extract(String srcPath, String destPath) {
		File srcFile = new File(srcPath);
		if (srcFile.isFile()) {
			extract(srcFile, destPath);
		}
	}

	public static void extract(File srcFile, String destPath) {
		extract(srcFile, new File(destPath));
	}

	public static void extract(File srcFile, File destFile) {
		try {
			TarArchiveInputStream tais = new TarArchiveInputStream(new FileInputStream(srcFile));
			extract(destFile, tais);
			tais.close();
		} catch (IOException e) {
			log.info(TAG, e.toString());
		}
	}

	private static void extract(File destFile, TarArchiveInputStream tais) {
		TarArchiveEntry entry = null;
		try {
			while ((entry = tais.getNextTarEntry()) != null) {
				String dirPath = destFile.getPath() + File.separator + entry.getName();
				File dirFile = new File(dirPath);
				createParentDirRecursively(dirFile);
				if (entry.isDirectory()) {
					dirFile.mkdirs();
				} else {
					extractFile(dirFile, tais);
				}
			}
		} catch (IOException e) {
			log.info(TAG, e.toString());
		}
	}

	private static void extractFile(File destFile, TarArchiveInputStream tais) {
		BufferedOutputStream bos;
		try {
			bos = new BufferedOutputStream(new FileOutputStream(destFile));
			int count;
			byte data[] = new byte[BUFFER];
			while ((count = tais.read(data, 0, BUFFER)) != -1) {
				bos.write(data, 0, count);
			}
			bos.close();
		} catch (FileNotFoundException e) {
			log.info(TAG, e.toString());
		} catch (IOException e) {
			log.info(TAG, e.toString());
		}
	}

	private static void createParentDirRecursively(File dirFile) {
		File parentFile = dirFile.getParentFile();
		if (!parentFile.exists()) {
			createParentDirRecursively(parentFile);
			parentFile.mkdir();
		}
	}

}
